import bcrypt
from pyramid.authentication import AuthTktCookieHelper
from pyramid.authorization import ACLHelper, Authenticated, Everyone
from .models import DBSession, Ftprecode


def hash_password(pw):
    pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
    return pwhash.decode('utf8')


def check_password(pw, hashed_pw):
    """
    检查提供的密码是否与给定的哈希密码匹配。

    参数:
    pw: str - 需要验证的明文密码。
    hashed_pw: str - 已经哈希处理的密码字符串。

    返回值:
    bool - 如果提供的密码与哈希密码匹配，则返回True；否则返回False。
    """
    # 将哈希密码转换为字节序列，以适配bcrypt的验证过程
    expected_hash = hashed_pw.encode('utf8')
    # 使用bcrypt库验证明文密码是否与哈希密码匹配
    return bcrypt.checkpw(pw.encode('utf8'), expected_hash)


class SecurityPolicy:
    def __init__(self, secret):
        self.authtkt = AuthTktCookieHelper(secret=secret)
        self.acl = ACLHelper()
        self.db_session = DBSession()

    def identity(self, request):
        """
        根据请求识别身份。

        参数:
        - self: 表示实例自身。
        - request: 表示一个请求对象。

        返回值:
        - 如果身份识别成功，则返回识别出的身份信息；否则返回None。
        """
        # 通过authtkt模块对请求进行身份识别
        identity = self.authtkt.identify(request)
        # 如果识别出身份，则返回该身份信息
        if identity is not None:
            return identity

    def authenticated_userid(self, request):
        """
        验证用户身份并返回用户ID。

        参数:
        - self: 方法的对象引用。
        - request: 代表客户端请求的对象。

        返回值:
        - 如果用户已通过身份验证，则返回用户的ID（字符串）；否则返回None。
        """
        identity = self.identity(request)  # 基于请求尝试获取用户身份信息
        # print("-" * 10, identity, "-" * 10)
        if identity is not None:
            return identity['userid']  # 如果身份信息存在，则返回用户ID

    def remember(self, request, userid, **kw):
        """
        记住用户身份的功能，用于在用户登录后设置cookie，以便用户在一段时间内无需再次登录。

        参数:
        - self: 方法的对象引用
        - request: 代表客户端请求的对象，用于存储和传递与请求相关的信息。
        - userid: 用户的唯一标识符，通常为用户名或用户ID。
        - **kw: 可变的关键字参数，用于传递额外的参数给remember方法。

        返回值:
        - 返回值为调用authtkt.remember方法的结果，通常为一个包含认证token的cookie字符串。
        """
        return self.authtkt.remember(request, userid, **kw)

    def forget(self, request, **kw):
        """
        忘记会话：这个方法用于生成一个“忘记”会话的令牌，即清除用户会话。

        参数:
        - self: 方法的对象引用。
        - request: 一个包含请求信息的对象，通常是从客户端发来的HTTP请求。
        - **kw: 可变的关键字参数，用于传递给内部调用的`authtkt.forget`方法的额外参数。

        返回值:
        - 返回值是调用`authtkt.forget`方法的结果，通常是一个表示忘记会话操作成功或失败的值。
        """
        return self.authtkt.forget(request, **kw)

    def permits(self, request, context, permission):
        """
        判断是否允许特定权限访问。

        参数:
        - request: 请求对象，用于获取当前的请求信息。
        - context: 上下文对象，提供访问控制列表（ACL）的环境背景。
        - permission: 需要验证的权限。

        返回值:
        - 返回一个布尔值，True表示允许访问，False表示不允许访问。
        """
        # 获取当前请求的有效主体
        # print("permits", permission, '!' * 100)
        principals = self.effective_principals(request)
        # print("permits-principals", principals, '!' * 100)
        # 基于上下文、有效主体和所需权限，检查是否允许访问
        return self.acl.permits(context, principals, permission)

    def effective_principals(self, request):
        """
        根据请求确定有效的主体（principals）。

        参数:
        - self: 方法的对象引用。
        - request: 代表web请求的对象，用于获取认证信息。

        返回值:
        - 返回一个列表，包含所有有效的主体标识符。
        """
        # 初始化主体列表，包括所有访问者
        principals = [Everyone]
        # 获取已认证的用户ID
        userid = self.authenticated_userid(request)

        if userid is not None:
            user = self.db_session.query(Ftprecode).filter_by(account=userid).first()
            # # 如果用户已认证，添加认证用户和全体认证用户主体
            # principals += [Authenticated, 'u:' + userid]
            # # 添加用户所属的组
            # principals += GROUPS.get(userid, [])

            principals.append('u:' + userid)
            principals.append('g:' + user.groupname)
            principals.append(Authenticated)

        # print("effective_principals", principals, '!' * 100)
        print(
            "-----------------用户组------------------\n\n\n",
            principals,
            "\n\n\n-----------------------------------"
        )
        return principals
